/*!
\copyright  Copyright (c) 2023 Qualcomm Technologies International, Ltd.
            All Rights Reserved.
            Qualcomm Technologies International, Ltd. Confidential and Proprietary.
\file       storage_manager_ra_partition.c
\brief      RA partition storage manager Implementation
*/

#include "storage_manager_ra_partition.h"

static const list_config_t ra_partition_entry_cfg = 
{
    .type = list_type_linked_single,
    .element_store.type = list_store_reference,
};

static list_t open_buffers = NULL;

static void storageManagerRaPartition_Init(void)
{
     open_buffers = ListCreate(&ra_partition_entry_cfg);
}

static storage_mgr_buffer_handle_t storageManagerRaPartition_OpenBuffer (storage_mgr_addr_data_t addr, const storage_mgr_event_if * events)
{
    const storage_mgr_addr_ra_partition_t * ra_addr = (storage_mgr_addr_ra_partition_t *)addr;
    raPartition_result ra_result = RA_PARTITION_RESULT_FAIL;
    ra_partition_buffer_t * store_buffer = NULL;
    ra_partition_buffer_t temp_buffer =  
    {
        .protected_data = 
        {   
            .interface = NULL, 
            .events = events
        },
        .ra_instance = {0},
    };

    switch (ra_addr->type)
    {
        case DEFAULT_PARTITION:
        {
            ra_result = RaPartitionOpen(&temp_buffer.ra_instance.handle, &temp_buffer.ra_instance.info);
            break;
        }
        case PARTITION_INSTANCE:
        {
            ra_result = RaPartitionInstanceOpen(ra_addr->instance, &temp_buffer.ra_instance.handle, &temp_buffer.ra_instance.info);
            break;
        }
        default:
            DEBUG_LOG_VERBOSE("storageManagerRaPartition_OpenBuffer: Unknown identifier type for opening ra partition:%d", ra_addr->type);
            break;
    }

    if (ra_result == RA_PARTITION_RESULT_SUCCESS)
    {
        store_buffer = PanicNull(calloc(1, sizeof(ra_partition_buffer_t)));
        memcpy(store_buffer, &temp_buffer, sizeof(ra_partition_buffer_t));
        ListAppend(&open_buffers, store_buffer);
    }
    return (storage_mgr_buffer_handle_t) store_buffer;
}

static bool storageManagerRaPartition_LoadData(storage_mgr_buffer_handle_t source, uint32 offset, uint8 * dest, size_t bytes)
{
    ra_partition_buffer_t * ra_buffer = (ra_partition_buffer_t *)source;
    /*
        The RaPartitionRead API states that any bytes beyond the partition size are read from the start (wraps around).
        Should the manager guard against this?
    */
    if ( (offset + bytes) > ra_buffer->ra_instance.info.partition_size)
    {
        return FALSE;
    }
    return RaPartitionRead(&ra_buffer->ra_instance.handle, offset, bytes, (uint8*)dest) == RA_PARTITION_RESULT_SUCCESS;
}

static bool storageManagerRaPartition_EraseAndWriteToPartition (ra_partition_buffer_t * ra_buffer, uint32 offset, const uint8 * source, size_t bytes)
{
    size_t block_start = (offset / ra_buffer->ra_instance.info.block_size) * ra_buffer->ra_instance.info.block_size;
    size_t block_stop = offset + bytes;

    while (block_start < block_stop)
    {
        if ( RaPartitionErase(&ra_buffer->ra_instance.handle, block_start) != RA_PARTITION_RESULT_SUCCESS )
        {
            DEBUG_LOG_VERBOSE("storageManagerRaPartition_EraseAndWriteToPartition: Failed to erase block at offset: %u", block_start);
            return FALSE;
        }

        block_start += ra_buffer->ra_instance.info.block_size;
    }
    return RaPartitionWrite(&ra_buffer->ra_instance.handle, offset, bytes, source) == RA_PARTITION_RESULT_SUCCESS;
}

static bool storageManagerRaPartition_SaveData(storage_mgr_buffer_handle_t dest, uint32 offset, const uint8 * source, size_t bytes)
{
    ra_partition_buffer_t * ra_buffer = (ra_partition_buffer_t *) dest;
    /*
        The RaPartitionWrite API does the same for excess bytes when writing (wraps around).
        Should the manager guard against this?
    */
    if ( (offset + bytes) > ra_buffer->ra_instance.info.partition_size)
    {
        return FALSE;
    }

    return storageManagerRaPartition_EraseAndWriteToPartition(ra_buffer, offset, (uint8*)source, bytes);
}

static inline raPartition_result storageManagerRaPartition_CloseBufferWithHandle(raPartition_handle * partition_handle)
{
    return RaPartitionClose(partition_handle);
}

static bool storageManagerRaPartition_CloseBuffer(storage_mgr_buffer_handle_t buffer)
{
    bool found_and_closed = FALSE;
    LIST_FOREACH(&open_buffers)
    {
        if ( (storage_mgr_buffer_handle_t)ListGetCurrentReference(&open_buffers) == buffer )
        {
            ra_partition_buffer_t * ra_buffer = (ra_partition_buffer_t *)buffer;
            found_and_closed = storageManagerRaPartition_CloseBufferWithHandle(&ra_buffer->ra_instance.handle) == RA_PARTITION_RESULT_SUCCESS;
            ListRemoveCurrentElement(&open_buffers);
            free(ra_buffer);
            break;
        }
    }
    return found_and_closed;
}

static bool storageManagerRaPartition_ForceCloseAllBuffers(void)
{
    bool did_close = TRUE;
    ListGotoHead(&open_buffers);
    while (!ListIsAtEndOfList(&open_buffers) && did_close)
    {
        ra_partition_buffer_t * ra_buffer = ListGetCurrentReference(&open_buffers);
        if (ra_buffer->protected_data.events && ra_buffer->protected_data.events->OnForceCloseBuffer)
        {
            ra_buffer->protected_data.events->OnForceCloseBuffer( (storage_mgr_buffer_handle_t) ra_buffer);
        }
        did_close = storageManagerRaPartition_CloseBufferWithHandle(&ra_buffer->ra_instance.handle) == RA_PARTITION_RESULT_SUCCESS;
        ListRemoveCurrentElement(&open_buffers);
        free(ra_buffer);
    }
    return did_close;
}

static size_t storageManagerRaPartition_GetBufferSize(storage_mgr_buffer_handle_t buffer)
{
    ra_partition_buffer_t * ra_buffer = (ra_partition_buffer_t *)buffer;
    return ra_buffer->ra_instance.info.partition_size;
}

const storage_mgr_interface_t storage_ra_partition =
{
    .StorageManager_Initialize = storageManagerRaPartition_Init,
    .StorageManager_OpenBuffer = storageManagerRaPartition_OpenBuffer,
    .StorageManager_LoadData = storageManagerRaPartition_LoadData,
    .StorageManager_SaveData = storageManagerRaPartition_SaveData,
    .StorageManager_CloseBuffer = storageManagerRaPartition_CloseBuffer,
    .StorageManager_ForceCloseAllBuffers = storageManagerRaPartition_ForceCloseAllBuffers,
    .StorageManager_GetBufferSize = storageManagerRaPartition_GetBufferSize, 
};


